//
//  url.m
//  iCodeMacRun
//
//  Created by Andrew Barry on 6/04/09.
//  Copyright 2009 Barry Computer Services. All rights reserved.
//

#include <curl/curl.h>
#import "url.h"
#import "cencode.h"

@interface httpDelegate : NSObject {
	NSMutableData *receivedData;
	jscontext *_cx;
	@public jsvalue scope;
	@public jsvalue handler;
}
@end
@implementation httpDelegate
-(id) initWithContext: (jscontext *) cx {
	self = [super init];
	_cx = cx;
	scope = INT_TO_JSVAL(0);
	handler = INT_TO_JSVAL(0);
	
	jsaddroot(cx, &handler);
	jsaddroot(cx, &scope);
	
	receivedData = [NSMutableData new];
	return self;
}

-(void) dealloc
{
	jsremoveroot(_cx, &handler);
	jsremoveroot(_cx, &scope);
	[super dealloc];
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [receivedData appendData:data];
}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
	jsvalue obj = jsnewobject(_cx);
	jsaddroot(_cx, &obj);
	jsset(_cx, obj, 0, "success", BOOLEAN_TO_JSVAL(0));
	jsinvoke(_cx, handler, scope, &obj, 1);
	jsremoveroot(_cx, &obj);
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
	jsvalue obj = jsnewobject(_cx);
	jsset(_cx, obj, 0, "success", BOOLEAN_TO_JSVAL(1));
	
	NSString *strdata = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
	jsset(_cx, obj, 0, "text", jsstrFromNSString(_cx, strdata));
	
	jsaddroot(_cx, &obj);
	jsinvoke(_cx, handler, scope, &obj, 1);
	jsremoveroot(_cx, &obj);
}
@end

static NSString *encodeURI(NSString *str)
{
	const char *utf8 = [str UTF8String];
	int len = strlen(utf8);
	char *resultBuffer = malloc(len * 3) + 1;
	int pos = 0;
	const char *hexdigit = "0123456789ABCDEF";
	
	int idx;
	for (idx = 0; utf8[idx]; idx++) {
		int ch = utf8[idx] & 0xff;
		if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
		{
			resultBuffer[pos++] = ch;
		}
		else
		{
			if (ch == ' ')
				resultBuffer[pos++] = '+';
			else {
				resultBuffer[pos++] = '%';
				resultBuffer[pos++] = hexdigit[ch >> 4];
				resultBuffer[pos++] = hexdigit[ch & 15];
			}
		}
	}
	resultBuffer[pos++] = 0;
	NSMutableString *result = [NSString stringWithUTF8String:resultBuffer];
	free(resultBuffer);
	return result;
}

static NSString *buildURLParams(jscontext *cx, jsobject *dataObj) {
	NSMutableString *buildPost = [[NSMutableString new] autorelease];
	bool first = YES;
	for (jsfield *f = dataObj->fields; f; f = f->next) {
		if (first)
			first = NO;
		else
			[buildPost appendString:@"&"];
		NSString *v = jsasstrRetain(cx, f->val);
		[buildPost appendString: encodeURI([NSString stringWithUTF8String:f->field])];
		[buildPost appendString:@"="];
		[buildPost appendString:encodeURI(v)];
		[v release];
	}
	return buildPost;
}

static jsvalue httpGet(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	if (argc < 3)
		return JSVAL_FALSE;
	
	NSString *url = nil;
	NSString *userId = nil;
	NSString *pwd = nil;

	if (JSVAL_IS_OBJECT(argv[0])) {
		jsvalue paramsval;
		jsvalue req = argv[0];
		url = jsgetstring(cx, req, 0, "url", @"");
		userId = jsgetstring(cx, req, 0, "username", nil);
		pwd = jsgetstring(cx, req, 0, "password", nil);
		paramsval = jsget(cx, req, 0, "params");
		
		if (JSVAL_IS_OBJECT(paramsval)) {
			NSString *encodedParams = buildURLParams(cx, JSVAL_TO_OBJECT(paramsval));
			NSRange r = [url rangeOfString:@"?"];
			if (r.location != NSNotFound) {
				url = [url stringByAppendingString:@"&"];
			} else {
				url = [url stringByAppendingString:@"?"];
			}
			url = [url stringByAppendingString:encodedParams];
		}
	} else {
		url = [jsasstrRetain(cx, argv[0]) autorelease];
	}
	if (!url) {
		return JSVAL_FALSE;
	}
	
	httpDelegate *delegate = [[httpDelegate alloc] initWithContext:cx];

	delegate->scope = argv[1];
	delegate->handler = argv[2];

	NSMutableURLRequest *request = [NSMutableURLRequest new];
	[request setURL:[NSURL URLWithString:url]];

	if (userId != nil) {
		if (pwd == nil)
			pwd = @"";
		NSString *combined = [NSString stringWithFormat:@"%@:%@", userId, pwd];
		NSString *encoded = base64Encode([combined dataUsingEncoding:NSUTF8StringEncoding]);
		
		[request addValue: [@"Basic " stringByAppendingString:encoded] forHTTPHeaderField: @"Authorization"];
	}
	
	NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:delegate];
	if (theConnection) {
	}
	return JSVAL_TRUE;
}

static jsvalue httpPost(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	if (argc < 4)
		return JSVAL_FALSE;
	
	NSString *url = nil;
	NSString *userId = nil;
	NSString *pwd = nil;
	
	if (JSVAL_IS_OBJECT(argv[0])) {
		jsvalue paramsval;
		jsvalue req = argv[0];
		url = jsgetstring(cx, req, 0, "url", @"");
		userId = jsgetstring(cx, req, 0, "username", nil);
		pwd = jsgetstring(cx, req, 0, "password", nil);
		paramsval = jsget(cx, req, 0, "params");
		if (JSVAL_IS_OBJECT(paramsval)) {
			NSString *encodedParams = buildURLParams(cx, JSVAL_TO_OBJECT(paramsval));
			NSRange r = [url rangeOfString:@"?"];
			if (r.location != NSNotFound) {
				url = [url stringByAppendingString:@"&"];
			} else {
				url = [url stringByAppendingString:@"?"];
			}
			url = [url stringByAppendingString:encodedParams];
		}
	} else {
		url = [jsasstrRetain(cx, argv[0]) autorelease];
	}
	if (!url) {
		return JSVAL_FALSE;
	}
	
	httpDelegate *delegate = [httpDelegate new];
	
	delegate->scope = argv[2];
	delegate->handler = argv[3];
	
	NSMutableURLRequest *request = [NSMutableURLRequest new];
	[request setURL:[NSURL URLWithString:url]];
	[request setHTTPMethod:@"POST"];
	
	NSString *postData;
	if (JSVAL_IS_OBJECT(argv[1])) {
		postData = buildURLParams(cx, JSVAL_TO_OBJECT(argv[1]));
	} else {
		postData = [jsasstrRetain(cx, argv[1]) autorelease];
	}		
	
	[request setHTTPBody:[postData dataUsingEncoding:NSUTF8StringEncoding]];
	
	if (userId != nil) {
		if (pwd == nil)
			pwd = @"";
		NSString *combined = [NSString stringWithFormat:@"%@:%@", userId, pwd];
		NSString *encoded = base64Encode([combined dataUsingEncoding:NSUTF8StringEncoding]);
		
		[request addValue: [@"Basic " stringByAppendingString:encoded] forHTTPHeaderField: @"Authorization"];
	}
	
	NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:delegate];
	if (theConnection) {
	}
	return JSVAL_TRUE;
}

static jsvalue curlUploadFile(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	NSString *url;
	NSString *userId = nil, *pwd = nil;
	
	if (argc < 2)
		return JSVAL_FALSE;
	
	if (JSVAL_IS_STRING(argv[0]))
		url = [jsasstrRetain(cx, argv[0]) autorelease];
	else if (JSVAL_IS_OBJECT(argv[0])) {
		jsvalue req = argv[0];
		url = jsgetstring(cx, req, 0, "url", @"");
		userId = jsgetstring(cx, req, 0, "username", nil);
		pwd = jsgetstring(cx, req, 0, "password", nil);
	}
	
	NSString *path = [jsasstrRetain(cx, argv[1]) autorelease];
	CURL *curl = curl_easy_init( );

	FILE *f = fopen([path UTF8String], "rb");

	curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE) ;
	if (userId != nil && pwd != nil) {
		NSString *userpwd = [NSString stringWithFormat:@"%@:%@", userId, pwd];
		curl_easy_setopt(curl, CURLOPT_USERPWD, [userpwd UTF8String]); // user:pwd
	}
	curl_easy_setopt(curl, CURLOPT_URL, [url UTF8String]);
	curl_easy_setopt(curl, CURLOPT_READDATA, f);
	
	curl_easy_perform(curl);
	fclose(f);
	
	curl_easy_cleanup(curl);
	return JSVAL_TRUE;
}

void registerUrl(jscontext *cx) {
	jsvalue HTTP, Curl;
	cx->slots[10] = HTTP = jsnewobject(cx);
	cx->slots[12] = Curl = jsnewobject(cx);
	
	jsset(cx, HTTP, 0, "get", bindFunction(cx, httpGet));
	jsset(cx, HTTP, 0, "post", bindFunction(cx, httpGet));
	jsset(cx, Curl, 0, "upload", bindFunction(cx, curlUploadFile));
}
